gates= {};
gates.max_distance = 32;
gates.max_volume = 2000;

local savedir = minetest.get_worldpath().."/gates/"

local template = [[
	size[8,6;]	
	label[0.1,0.0;Gate Controller]
	box[0.1,0.6;7.5,0.05;#FFFFFF]
	box[0.1,5;7.5,0.05;#FFFFFF]
]]

if default and default.gui_bg then 
	template = template..default.gui_bg;
end

if default and default.gui_bg_img then 
	template = template..default.gui_bg_img;
end

if default and default.gui_slots then 
	template = template..default.gui_slots;
end

local form_page0 = template .. [[
	label[0.1,1;Thanks for Choosing addi's gates mod. 
This Assistant will help you to setup the Gate Controller

To continue click Next]

	button[4.9,5.6;3,0;next;Next-->]
]]

local form_page1 = template .. [[
	label[0.1,0.7;First, you need to set the area within the gate is acting.
Attention: please think about that the area have to contain 
the closed and the opened gate!]
							
	field[0.4,3;2,0;pos1;Position 1 : (x,y,z);%s]
	button[2,2.7;5,0;cb_read1;Read pos1 of your last punched node]
							
	field[0.4,4;2,0;pos2;Position 2 : (x,y,z);%s]
	button[2,3.7;5,0;cb_read2;Read pos2 of your last punched node]
							
	button[0.1,5.6;3,0;prev;<--Back] button[4.9,5.6;3,0;next;Next-->]
]]

local form_page2 = template ..[[
	label[0.1,1;Now its time for Building. :-)
	You have to build the gate twice. 
	
	Do you want to build the opened or the closed Gate first?]
	
	button[0.5,3;7,0;build_opend;I want to build the opened gate first!]
	
	button[0.5,4;7,0;build_closed;Ill start with the closed one!]
	
	button[0.1,5.6;3,0;prev;<--Back]
]]

local form_page2_opend = template ..[[
	label[0.1,1;Wonderful, now you have to to build a gate wich is opend.
	
If you are done, click Next]
	
	button[0.1,5.6;3,0;prev;<--Back] button[4.9,5.6;3,0;opend_finisched;Next-->]	
]]

local form_page2_closed = template ..[[
	label[0.1,1;OK, The next step is to build the gate wich is closed.

If you are done, click Next]
	
	button[0.1,5.6;3,0;prev;<--Back] button[4.9,5.6;3,0;closed_finisched;Next-->]
]]
local form_page3 = template .. [[
	label[0.1,1;Congratulation! the setup is complete, and the controller is ready.
Currently the gate is %s

Click on the button toggle or punch this node to switch.
If mesecons installed you can also use mesecons.]

	button[0.5,4;7,0;toggle;Toggle (switch between open and closed gate)]
	
	button[0.1,5.6;3,0;setup;Re-Start Setup]
]]

if minetest.mkdir then
	minetest.mkdir(savedir)
else
	os.execute('mkdir "'..savedir..'"')
end

local function schematic_exists(pos,num)
	local name = savedir.. minetest.pos_to_string(pos).."_"..num..".schematic"
   local f=io.open(name,"r")
   if f~=nil then io.close(f) return true else return false end
end

gates.cache = {}

function gates.create_schematic(p1, p2, filename)
	minetest.create_schematic(p1, p2,{},savedir.. filename)
	--gates.cache[filename] = minetest.serialize_schematic(savedir..filename, "lua", {})
	--minetest.log("error", gates.cache[filename])
end

function gates.place_schematic(p, filename)
	local schematic = minetest.serialize_schematic(savedir..filename, "lua", {}) or ""
	-- load filename and serialize to string. append return{} so that the table can read by deserialize
	local schematic_STR = "return { "..schematic.."}"
	-- get the schematic by deserializing the shematic-string
	local schematic = minetest.deserialize(schematic_STR)
	-- finaly place the shematic
	return minetest.place_schematic(p, schematic.schematic, nil, nil, true)
end

local function sort_pos(pos1, pos2)
	if pos1.x > pos2.x then
		pos2.x, pos1.x = pos1.x, pos2.x
	end
	if pos1.y > pos2.y then
		pos2.y, pos1.y = pos1.y, pos2.y
	end
	if pos1.z > pos2.z then
		pos2.z, pos1.z = pos1.z, pos2.z
	end
	return pos1, pos2
end

local function volume(pos1, pos2)
	return (pos2.x - pos1.x + 1) *
		   (pos2.y - pos1.y + 1) *
		   (pos2.z - pos1.z + 1)
end

local function get_form(pos)
	local meta = minetest.get_meta(pos);
	local page = meta:get_int("page");
	
	local form = ""
	
	if page == 1 then
		form = form_page1:format(meta:get_string("pos1"),meta:get_string("pos2"));
		if worldedit then
			form = form .. 	"button[0.1,4.5;7.8,0;read_worldedit;Read pos1 and pos2 from worldedit]"
		end

	elseif page == 2 then --display page 2
		local state = meta:get_string("state")
		
		if state  == "closed" then
			form = form_page2_closed
		elseif state == "opend" then
			form = form_page2_opend
		else
			form = form_page2
		end
	elseif page >= 3 then --page 3: Finished
		local state = meta:get_string("state");
		form = form_page3:format(state)
	else	--page 0 Init
		form = form_page0
	end
	return form
end

local function placed(pos, placer, itemstack, pointed_thing)
	local meta = minetest.get_meta(pos);
	local form = get_form(pos);
	meta:set_string("formspec",form);
	meta:set_string("owner",placer:get_player_name());
	meta:set_string("infotext","Right click to init");
end

local function close_gate(pos)
	--local start_time = os.clock()
	local meta = minetest.get_meta(pos);
	if meta:get_string("state") == "opend" then -- only close the gate if its closed
		local pos1 = minetest.string_to_pos(meta:get_string("pos1"))
		local pos2 = minetest.string_to_pos(meta:get_string("pos2"))
		gates.create_schematic(pos1, pos2, minetest.pos_to_string(pos).."_opend.schematic")--save the opend gate before closing
		if gates.place_schematic(pos1, minetest.pos_to_string(pos).."_closed.schematic") then
			meta:set_string("state","closed");
			meta:set_string("infotext","closed");
			minetest.swap_node(pos, {name="gates:controler_closed"})
		else
			minetest.log("error","GATES: Could not place shematic")
		end
	end
	--local end_time = os.clock()
	--elapsed_time = end_time-start_time
	--print('start time: '   .. start_time .. 's')
	--print('end time: '     .. end_time .. 's')
--print('time elapsed: ' .. elapsed_time .. 's')
end

local function open_gate(pos)
	--local start_time = os.clock()
	local meta = minetest.get_meta(pos);
	if meta:get_string("state") == "closed" then -- only open the gate if its closed
		local pos1 = minetest.string_to_pos(meta:get_string("pos1"))
		local pos2 = minetest.string_to_pos(meta:get_string("pos2"))
		gates.create_schematic(pos1, pos2, minetest.pos_to_string(pos).."_closed.schematic")--save the closed gate before opening
		if gates.place_schematic(pos1, minetest.pos_to_string(pos).."_opend.schematic") then
			meta:set_string("state","opend");
			meta:set_string("infotext","opend");
			minetest.swap_node(pos, {name="gates:controler_opend"})
		else
			minetest.log("error","GATES: Could not place shematic")
		end
	end
	--local end_time = os.clock()
	--elapsed_time = end_time-start_time
	--print('start time: '   .. start_time .. 's')
	--print('end time: '     .. end_time .. 's')
	--print('time elapsed: ' .. elapsed_time .. 's')
end

local function toggle(pos)
	local meta = minetest.get_meta(pos);
	if meta:get_string("state") == "opend" then
		close_gate(pos);
		return "closed"
	else
		open_gate(pos);
		return "opend"
	end
end

local function destruct(pos)
	os.remove(savedir..minetest.pos_to_string(pos).."_opend.schematic")
	os.remove(savedir..minetest.pos_to_string(pos).."_closed.schematic")
end

local msg_too_far = "%s is too far away from gate controler. Max %d is alowed, you have %g distance!"
local  function submitted(pos, formname, fields, sender)
if fields.quit then return end;
	meta = minetest.get_meta(pos);
	page = meta:get_int("page") or 0;
	
	if fields.next and page == 0 then
		meta:set_int("page",1);
	elseif fields.next and fields.pos1 and fields.pos2 then --check if correct node is entered
		local pos1 = minetest.string_to_pos(fields.pos1);
		if pos1 == nil then
			minetest.chat_send_player(sender:get_player_name(), "Not a valid Position 1 entered. Have to be in format (x,y,z)")
			return 
		end
		local pos2 = minetest.string_to_pos(fields.pos2);
		if pos2 == nil then
			minetest.chat_send_player(sender:get_player_name(), "Not a valid Position 2 entered. Have to be in format (x,y,z)")
			return 
		end
		--print(vector.distance(pos, pos1));
		--print(gates.max_distance);
		if vector.distance(pos, pos1) > gates.max_distance then 
			minetest.chat_send_player(sender:get_player_name(), msg_too_far:format("Pos1", gates.max_distance, vector.distance(pos, pos1) ))
			return
		end
		if vector.distance(pos, pos2) > gates.max_distance then 
			minetest.chat_send_player(sender:get_player_name(), msg_too_far:format("Pos2", gates.max_distance, vector.distance(pos, pos2) ))
			return
		end
		local smallest,biggest = sort_pos(pos1,pos2)
		local volume = volume(smallest, biggest)
		--print("volume:",volume);
		if volume > gates.max_volume then		
			minetest.chat_send_player(sender:get_player_name(),
										string.format("The volume is too big! Max %d is alowed. Your volume would be %d.", gates.max_volume, volume)
										)
		return
		end
		meta:set_string("pos1", minetest.pos_to_string(smallest))
		meta:set_string("pos2", minetest.pos_to_string(biggest))
		meta:set_int("page",2);

	end
	 
	if fields.build_opend then
		meta:set_string("state","opend");
		meta:set_string("infotext","Now build a gate wich is open.");
	elseif fields.build_closed then
		meta:set_string("state","closed");
		meta:set_string("infotext","Now build a gate wich is closed.");
	end
	
	if fields.opend_finisched then
		local pos1 = minetest.string_to_pos(meta:get_string("pos1"))
		local pos2 = minetest.string_to_pos(meta:get_string("pos2"))
		minetest.create_schematic(pos1, pos2,{}, savedir..minetest.pos_to_string(pos).."_opend.schematic")
		if schematic_exists(pos,"closed") then --if there is a closed one build
			meta:set_int("page",3);--set page to page 3
			meta:set_string("infotext","opend");
			minetest.swap_node(pos, {name="gates:controler_opend"})
		else 
			meta:set_string("state","closed");
			meta:set_string("infotext","Now build a gate wich is closed.");
		end
	elseif fields.closed_finisched then
		local pos1 = minetest.string_to_pos(meta:get_string("pos1"))
		local pos2 = minetest.string_to_pos(meta:get_string("pos2"))
		minetest.create_schematic(pos1, pos2,{}, savedir..minetest.pos_to_string(pos).."_closed.schematic")
		if schematic_exists(pos,"opend") then --if there is a opend one build
			meta:set_int("page",3);--set page to page 3
			meta:set_string("infotext","closed");
			minetest.swap_node(pos, {name="gates:controler_closed"})
		else 
			meta:set_string("state","opend");
			meta:set_string("infotext","Now build a gate wich is opend.");
		end
	end
	if fields.read_worldedit then
	local name = sender:get_player_name()
		minetest.chat_send_player(name, "read worldedit");
		local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name];
		if pos1 and pos2 then
			meta:set_string("pos1",minetest.pos_to_string(pos1));
			meta:set_string("pos2",minetest.pos_to_string(pos2));
		else
			minetest.chat_send_player(name, "No worldedit region selected");
		end
	end
	if fields.cb_read1 then
	local name = sender:get_player_name()
		--minetest.chat_send_player(name, "read from clipboard");
		local pos1 = gates.clipboard[name];
		if pos1  then
			meta:set_string("pos1",minetest.pos_to_string(pos1));		
		else
			minetest.chat_send_player(name, "Please punch (leftclick) a block");
		end
	end
	if fields.cb_read2 then
	local name = sender:get_player_name()
		--minetest.chat_send_player(name, "read from clipboard");
		local pos2 = gates.clipboard[name];
		if pos2  then
			meta:set_string("pos2",minetest.pos_to_string(pos2));		
		else
			minetest.chat_send_player(name, "Please punch (leftclick) a block");
		end
	end
	if fields.toggle then
		local status = toggle(pos);
		if status == "opend" then
		minetest.chat_send_player(sender:get_player_name(), "Gate Opened")
		elseif status == "closed" then
		minetest.chat_send_player(sender:get_player_name(), "Gate Closed")
		else
		minetest.chat_send_player(sender:get_player_name(), "ERROR: Unknown state of Gate")
		end
	end
	if fields.setup then
		meta:set_int("page",0);
		meta:set_string("state","?");
		minetest.swap_node(pos, {name="gates:controler"})
		destruct(pos)
	end
	if fields.prev then
		meta:set_int("page",page-1);
	end
	meta:set_string("formspec",get_form(pos))
end

local function punched(pos, node, puncher, pointed_thing)
	--print(schematic_exists(pos,"closed"),schematic_exists(pos,"opend"))
	local playername = puncher:get_player_name()
	local status = toggle(pos);
	if status == "opend" then
		minetest.chat_send_player(playername, "Gate Opened")
	elseif status == "closed" then
		minetest.chat_send_player(playername, "Gate Closed")
	else
		minetest.chat_send_player(playername, "ERROR: Unknown state of Gate")
	end	
end



minetest.register_node("gates:controler",{
	description ="Gate controller",
	tiles = {"gates_bg.png","gates_bg.png","gates_bg.png^gates_unknown.png"},
	groups = {oddly_breakable_by_hand = 1},
	after_place_node = placed,
	on_receive_fields = submitted,
	on_destruct = destruct,--function(pos, node, puncher, pointed_thing)
	mesecons = {
		effector = {
			rules = {
				{x = 1, y = 0, z = 0},
				{x =-1, y = 0, z = 0},
				{x = 0, y = 0, z = 1},
				{x = 0, y = 0, z =-1}
			},
			--action_on = open_gate,
			--action_off = close_gate--function (pos, node)
			--action_change = function (pos, node)
		}
	}
})

minetest.register_node("gates:controler_opend",{
	description ="Gate controller (you hacker you!)",
	tiles = {"gates_bg.png","gates_bg.png","gates_bg.png^gates_opend.png"},
	groups = {oddly_breakable_by_hand = 1, not_in_craft_guide = 1, not_in_creative_inventory = 1},
	after_place_node = placed,
	on_receive_fields =submitted,
	on_punch = punched,
	on_destruct = destruct,--function(pos, node, puncher, pointed_thing)
	drop = "gates:controler",
	mesecons = {
		effector = {
			rules = {
				{x = 1, y = 0, z = 0},
				{x =-1, y = 0, z = 0},
				{x = 0, y = 0, z = 1},
				{x = 0, y = 0, z =-1}
			},
			action_on = open_gate,
			action_off = close_gate--function (pos, node)
			--action_change = function (pos, node)
		}
	}
})

minetest.register_node("gates:controler_closed",{
	description ="Gate controller (you hacker you!)",
	tiles = {"gates_bg.png","gates_bg.png","gates_bg.png^gates_closed.png"},
	groups = {oddly_breakable_by_hand = 1, not_in_craft_guide = 1, not_in_creative_inventory = 1},
	after_place_node = placed,
	on_receive_fields =submitted,
	on_punch = punched,
	on_destruct = destruct,--function(pos, node, puncher, pointed_thing)
	drop = "gates:controler",
	mesecons = {
		effector = {
			rules = {
				{x = 1, y = 0, z = 0},
				{x =-1, y = 0, z = 0},
				{x = 0, y = 0, z = 1},
				{x = 0, y = 0, z =-1}
			},
			action_on = open_gate,
			action_off = close_gate--function (pos, node)
			--action_change = function (pos, node)
		}
	}
})

--clipboard action:
gates.clipboard = {};

minetest.register_on_punchnode(function(pos, node, puncher, pointed_thing)
	if node.name ~= "air" then
		local name = puncher:get_player_name();
		if gates.clipboard then
		gates.clipboard[name] = pos;
		else 
		gates.clipboard={};
		gates.clipboard[name] = pos;
		end
		--print("saved pos to clipboard");
	end
end)